"""
代码优化器核心模块
负责分析和优化代码
"""

import os
import re
import ast
import logging
from typing import Dict, List, Any, Optional, Tuple, Set, Callable

logger = logging.getLogger(__name__)

class CodeAnalyzer:
    """代码分析器，用于分析代码结构和质量"""
    
    def __init__(self, config: Optional[Dict[str, Any]] = None):
        """
        初始化代码分析器
        
        Args:
            config: 配置字典
        """
        self.config = config or {}
        self.issues = []
    
    def analyze_file(self, file_path: str) -> Dict[str, Any]:
        """
        分析单个文件
        
        Args:
            file_path: 文件路径
            
        Returns:
            分析结果
        """
        if not os.path.exists(file_path):
            return {"error": f"文件不存在: {file_path}"}
        
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            
            file_ext = os.path.splitext(file_path)[1].lower()
            
            if file_ext == '.py':
                return self._analyze_python(file_path, content)
            elif file_ext in ['.js', '.ts']:
                return self._analyze_javascript(file_path, content)
            else:
                return {"warning": f"不支持的文件类型: {file_ext}"}
        
        except Exception as e:
            logger.error(f"分析文件失败: {e}")
            return {"error": str(e)}
    
    def _analyze_python(self, file_path: str, content: str) -> Dict[str, Any]:
        """
        分析Python文件
        
        Args:
            file_path: 文件路径
            content: 文件内容
            
        Returns:
            分析结果
        """
        result = {
            "file_path": file_path,
            "language": "python",
            "issues": [],
            "metrics": {},
            "imports": [],
            "classes": [],
            "functions": []
        }
        
        try:
            # 解析AST
            tree = ast.parse(content)
            
            # 收集导入
            for node in ast.walk(tree):
                if isinstance(node, ast.Import):
                    for name in node.names:
                        result["imports"].append({"name": name.name, "alias": name.asname})
                elif isinstance(node, ast.ImportFrom):
                    module = node.module or ""
                    for name in node.names:
                        result["imports"].append({
                            "name": f"{module}.{name.name}" if module else name.name,
                            "alias": name.asname,
                            "from_import": True
                        })
            
            # 收集类
            for node in ast.walk(tree):
                if isinstance(node, ast.ClassDef):
                    class_info = {
                        "name": node.name,
                        "line": node.lineno,
                        "methods": [],
                        "bases": [self._get_name(base) for base in node.bases]
                    }
                    
                    # 收集方法
                    for child in node.body:
                        if isinstance(child, ast.FunctionDef):
                            method_info = {
                                "name": child.name,
                                "line": child.lineno,
                                "args": [arg.arg for arg in child.args.args],
                                "complexity": self._calculate_complexity(child)
                            }
                            class_info["methods"].append(method_info)
                    
                    result["classes"].append(class_info)
            
            # 收集函数
            for node in ast.walk(tree):
                if isinstance(node, ast.FunctionDef) and not isinstance(node.parent, ast.ClassDef):
                    func_info = {
                        "name": node.name,
                        "line": node.lineno,
                        "args": [arg.arg for arg in node.args.args],
                        "complexity": self._calculate_complexity(node)
                    }
                    result["functions"].append(func_info)
            
            # 计算度量指标
            result["metrics"] = {
                "loc": len(content.splitlines()),
                "classes": len(result["classes"]),
                "functions": len(result["functions"]),
                "imports": len(result["imports"])
            }
            
            # 检查问题
            result["issues"] = self._check_python_issues(content, tree)
            
            return result
        
        except SyntaxError as e:
            result["error"] = f"语法错误: {e}"
            return result
        except Exception as e:
            result["error"] = f"分析失败: {e}"
            return result
    
    def _analyze_javascript(self, file_path: str, content: str) -> Dict[str, Any]:
        """
        分析JavaScript/TypeScript文件
        
        Args:
            file_path: 文件路径
            content: 文件内容
            
        Returns:
            分析结果
        """
        result = {
            "file_path": file_path,
            "language": "javascript",
            "issues": [],
            "metrics": {},
            "imports": [],
            "classes": [],
            "functions": []
        }
        
        # 简单的正则表达式分析（实际项目中应使用专门的JS解析器）
        
        # 检测导入
        import_pattern = r'(?:import|require)\s*\(?[\s\n]*[\'"]([^\'"]+)[\'"]'
        imports = re.findall(import_pattern, content)
        result["imports"] = [{"name": imp} for imp in imports]
        
        # 检测类
        class_pattern = r'class\s+(\w+)(?:\s+extends\s+(\w+))?\s*{'
        classes = re.findall(class_pattern, content)
        for class_name, parent_class in classes:
            class_info = {
                "name": class_name,
                "bases": [parent_class] if parent_class else []
            }
            result["classes"].append(class_info)
        
        # 检测函数
        function_pattern = r'(?:function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>)'
        functions = re.findall(function_pattern, content)
        for func_match in functions:
            func_name = func_match[0] if func_match[0] else func_match[1]
            if func_name:
                result["functions"].append({"name": func_name})
        
        # 计算度量指标
        result["metrics"] = {
            "loc": len(content.splitlines()),
            "classes": len(result["classes"]),
            "functions": len(result["functions"]),
            "imports": len(result["imports"])
        }
        
        # 检查问题
        result["issues"] = self._check_javascript_issues(content)
        
        return result
    
    def _get_name(self, node: ast.AST) -> str:
        """
        获取AST节点的名称
        
        Args:
            node: AST节点
            
        Returns:
            节点名称
        """
        if isinstance(node, ast.Name):
            return node.id
        elif isinstance(node, ast.Attribute):
            return f"{self._get_name(node.value)}.{node.attr}"
        else:
            return "unknown"
    
    def _calculate_complexity(self, node: ast.AST) -> int:
        """
        计算代码复杂度（简化的圈复杂度）
        
        Args:
            node: AST节点
            
        Returns:
            复杂度值
        """
        complexity = 1  # 基础复杂度
        
        for child in ast.walk(node):
            if isinstance(child, (ast.If, ast.While, ast.For, ast.Try, ast.ExceptHandler)):
                complexity += 1
            elif isinstance(child, ast.BoolOp) and isinstance(child.op, (ast.And, ast.Or)):
                complexity += len(child.values) - 1
        
        return complexity
    
    def _check_python_issues(self, content: str, tree: ast.AST) -> List[Dict[str, Any]]:
        """
        检查Python代码问题
        
        Args:
            content: 文件内容
            tree: AST树
            
        Returns:
            问题列表
        """
        issues = []
        
        # 检查行长度
        max_line_length = self.config.get("max_line_length", 100)
        for i, line in enumerate(content.splitlines(), 1):
            if len(line) > max_line_length:
                issues.append({
                    "type": "line_too_long",
                    "line": i,
                    "message": f"行长度超过{max_line_length}个字符",
                    "severity": "warning"
                })
        
        # 检查复杂函数
        max_complexity = self.config.get("max_complexity", 10)
        for node in ast.walk(tree):
            if isinstance(node, ast.FunctionDef):
                complexity = self._calculate_complexity(node)
                if complexity > max_complexity:
                    issues.append({
                        "type": "function_too_complex",
                        "line": node.lineno,
                        "message": f"函数'{node.name}'复杂度为{complexity}，超过阈值{max_complexity}",
                        "severity": "warning"
                    })
        
        # 检查未使用的导入
        imported_names = set()
        for node in ast.walk(tree):
            if isinstance(node, ast.Import):
                for name in node.names:
                    imported_names.add(name.asname or name.name)
            elif isinstance(node, ast.ImportFrom):
                for name in node.names:
                    imported_names.add(name.asname or name.name)
        
        used_names = set()
        for node in ast.walk(tree):
            if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load):
                used_names.add(node.id)
        
        for name in imported_names:
            if name not in used_names and not name.startswith("_"):
                # 找到导入的行号
                for node in ast.walk(tree):
                    if isinstance(node, (ast.Import, ast.ImportFrom)):
                        for n in node.names:
                            if (n.asname or n.name) == name:
                                issues.append({
                                    "type": "unused_import",
                                    "line": node.lineno,
                                    "message": f"未使用的导入: {name}",
                                    "severity": "warning"
                                })
                                break
        
        return issues
    
    def _check_javascript_issues(self, content: str) -> List[Dict[str, Any]]:
        """
        检查JavaScript代码问题
        
        Args:
            content: 文件内容
            
        Returns:
            问题列表
        """
        issues = []
        
        # 检查行长度
        max_line_length = self.config.get("max_line_length", 100)
        for i, line in enumerate(content.splitlines(), 1):
            if len(line) > max_line_length:
                issues.append({
                    "type": "line_too_long",
                    "line": i,
                    "message": f"行长度超过{max_line_length}个字符",
                    "severity": "warning"
                })
        
        # 检查console.log语句
        console_pattern = r'console\.log\('
        for i, line in enumerate(content.splitlines(), 1):
            if re.search(console_pattern, line):
                issues.append({
                    "type": "console_log",
                    "line": i,
                    "message": "生产代码中不应包含console.log语句",
                    "severity": "warning"
                })
        
        # 检查TODO注释
        todo_pattern = r'//\s*TODO'
        for i, line in enumerate(content.splitlines(), 1):
            if re.search(todo_pattern, line):
                issues.append({
                    "type": "todo_comment",
                    "line": i,
                    "message": "代码中包含TODO注释",
                    "severity": "info"
                })
        
        return issues

class CodeOptimizer:
    """代码优化器，用于优化代码"""
    
    def __init__(self, config: Optional[Dict[str, Any]] = None):
        """
        初始化代码优化器
        
        Args:
            config: 配置字典
        """
        self.config = config or {}
        self.analyzer = CodeAnalyzer(config)
    
    def optimize_file(self, file_path: str) -> Dict[str, Any]:
        """
        优化单个文件
        
        Args:
            file_path: 文件路径
            
        Returns:
            优化结果
        """
        if not os.path.exists(file_path):
            return {"error": f"文件不存在: {file_path}"}
        
        try:
            # 分析文件
            analysis = self.analyzer.analyze_file(file_path)
            
            # 如果分析出错，返回错误
            if "error" in analysis:
                return analysis
            
            # 读取文件内容
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            
            # 根据文件类型选择优化方法
            if analysis["language"] == "python":
                optimized_content, changes = self._optimize_python(content, analysis)
            elif analysis["language"] == "javascript":
                optimized_content, changes = self._optimize_javascript(content, analysis)
            else:
                return {"warning": f"不支持优化的语言: {analysis['language']}"}
            
            # 如果内容有变化，写回文件
            if content != optimized_content:
                with open(file_path, 'w', encoding='utf-8') as f:
                    f.write(optimized_content)
            
            return {
                "file_path": file_path,
                "language": analysis["language"],
                "changes": changes,
                "issues_fixed": len(changes),
                "issues_remaining": len(analysis["issues"]) - len(changes)
            }
        
        except Exception as e:
            logger.error(f"优化文件失败: {e}")
            return {"error": str(e)}
    
    def optimize_directory(self, directory: str, file_pattern: str = "*.*") -> Dict[str, Any]:
        """
        优化目录中的所有文件
        
        Args:
            directory: 目录路径
            file_pattern: 文件匹配模式
            
        Returns:
            优化结果
        """
        import glob
        
        if not os.path.exists(directory) or not os.path.isdir(directory):
            return {"error": f"目录不存在: {directory}"}
        
        results = {
            "directory": directory,
            "files_processed": 0,
            "files_optimized": 0,
            "total_issues_fixed": 0,
            "total_issues_remaining": 0,
            "file_results": []
        }
        
        # 获取匹配的文件
        pattern = os.path.join(directory, "**", file_pattern)
        files = glob.glob(pattern, recursive=True)
        
        # 过滤掉不需要处理的文件和目录
        exclude_dirs = self.config.get("exclude_dirs", ["venv", "node_modules", ".git"])
        exclude_files = self.config.get("exclude_files", [])
        
        filtered_files = []
        for file in files:
            if os.path.isfile(file):
                # 检查是否在排除目录中
                exclude = False
                for exclude_dir in exclude_dirs:
                    if exclude_dir in file.split(os.sep):
                        exclude = True
                        break
                
                # 检查是否是排除文件
                for exclude_pattern in exclude_files:
                    if re.search(exclude_pattern, file):
                        exclude = True
                        break
                
                if not exclude:
                    filtered_files.append(file)
        
        # 处理每个文件
        for file in filtered_files:
            result = self.optimize_file(file)
            results["files_processed"] += 1
            
            if "changes" in result and result["changes"]:
                results["files_optimized"] += 1
                results["total_issues_fixed"] += result.get("issues_fixed", 0)
            
            results["total_issues_remaining"] += result.get("issues_remaining", 0)
            results["file_results"].append(result)
        
        return results
    
    def _optimize_python(self, content: str, analysis: Dict[str, Any]) -> Tuple[str, List[Dict[str, Any]]]:
        """
        优化Python代码
        
        Args:
            content: 文件内容
            analysis: 分析结果
            
        Returns:
            优化后的内容和变更列表
        """
        changes = []
        lines = content.splitlines()
        
        # 处理未使用的导入
        unused_imports = [issue for issue in analysis["issues"] if issue["type"] == "unused_import"]
        if unused_imports:
            # 按行号降序排序，避免删除行后影响后续行号
            for issue in sorted(unused_imports, key=lambda x: x["line"], reverse=True):
                line_idx = issue["line"] - 1
                if line_idx < len(lines):
                    # 记录变更
                    changes.append({
                        "type": "remove_unused_import",
                        "line": issue["line"],
                        "original": lines[line_idx],
                        "message": issue["message"]
                    })
                    
                    # 删除或注释该行
                    if self.config.get("comment_instead_of_remove", False):
                        lines[line_idx] = f"# {lines[line_idx]}  # 未使用的导入"
                    else:
                        # 如果是多行导入的一部分，只删除未使用的部分
                        import_line = lines[line_idx]
                        if "," in import_line:
                            # 提取导入名称
                            import_name = issue["message"].split(": ")[1]
                            # 尝试从导入语句中删除该名称
                            pattern = rf'(?:^|,)\s*{re.escape(import_name)}\s*(?:,|$)'
                            new_line = re.sub(pattern, ',', import_line)
                            # 清理多余的逗号
                            new_line = re.sub(r',\s*,', ',', new_line)
                            new_line = re.sub(r'^\s*import\s*,', 'import ', new_line)
                            new_line = re.sub(r',\s*, '', new_line)
                            lines[line_idx] = new_line
                        else:
                            # 整行删除
                            lines[line_idx] = ""
        
        # 处理行太长的问题
        long_lines = [issue for issue in analysis["issues"] if issue["type"] == "line_too_long"]
        if long_lines and self.config.get("fix_long_lines", True):
            for issue in long_lines:
                line_idx = issue["line"] - 1
                if line_idx < len(lines):
                    original_line = lines[line_idx]
                    # 尝试优化长行
                    optimized_line = self._optimize_long_line(original_line)
                    if optimized_line != original_line:
                        lines[line_idx] = optimized_line
                        changes.append({
                            "type": "fix_long_line",
                            "line": issue["line"],
                            "original": original_line,
                            "optimized": optimized_line,
                            "message": issue["message"]
                        })
        
        # 合并行并返回
        return "\n".join(lines), changes
    
    def _optimize_javascript(self, content: str, analysis: Dict[str, Any]) -> Tuple[str, List[Dict[str, Any]]]:
        """
        优化JavaScript代码
        
        Args:
            content: 文件内容
            analysis: 分析结果
            
        Returns:
            优化后的内容和变更列表
        """
        changes = []
        lines = content.splitlines()
        
        # 处理console.log语句
        console_logs = [issue for issue in analysis["issues"] if issue["type"] == "console_log"]
        if console_logs and self.config.get("remove_console_logs", True):
            for issue in sorted(console_logs, key=lambda x: x["line"], reverse=True):
                line_idx = issue["line"] - 1
                if line_idx < len(lines):
                    # 记录变更
                    changes.append({
                        "type": "remove_console_log",
                        "line": issue["line"],
                        "original": lines[line_idx],
                        "message": issue["message"]
                    })
                    
                    # 注释该行
                    if self.config.get("comment_instead_of_remove", True):
                        lines[line_idx] = f"// {lines[line_idx]}"
                    else:
                        # 检查是否是多行语句的一部分
                        if lines[line_idx].strip().endswith(";"):
                            # 单行语句，可以安全删除
                            lines[line_idx] = ""
                        else:
                            # 多行语句，转为注释更安全
                            lines[line_idx] = f"// {lines[line_idx]}"
        
        # 处理行太长的问题
        long_lines = [issue for issue in analysis["issues"] if issue["type"] == "line_too_long"]
        if long_lines and self.config.get("fix_long_lines", True):
            for issue in long_lines:
                line_idx = issue["line"] - 1
                if line_idx < len(lines):
                    original_line = lines[line_idx]
                    # 尝试优化长行
                    optimized_line = self._optimize_long_line_js(original_line)
                    if optimized_line != original_line:
                        lines[line_idx] = optimized_line
                        changes.append({
                            "type": "fix_long_line",
                            "line": issue["line"],
                            "original": original_line,
                            "optimized": optimized_line,
                            "message": issue["message"]
                        })
        
        # 合并行并返回
        return "\n".join(lines), changes
    
    def _optimize_long_line(self, line: str) -> str:
        """
        优化过长的Python行
        
        Args:
            line: 原始行
            
        Returns:
            优化后的行
        """
        max_length = self.config.get("max_line_length", 100)
        if len(line) <= max_length:
            return line
        
        # 检查是否是导入语句
        if re.match(r'^\s*(?:from|import)\s+', line):
            # 处理导入语句
            if "import" in line and "," in line:
                # 将逗号分隔的导入拆分为多行
                parts = line.split("import ")
                if len(parts) == 2:
                    prefix = parts[0] + "import "
                    imports = parts[1].split(",")
                    indented_imports = [imp.strip() for imp in imports]
                    return prefix + "(\n    " + ",\n    ".join(indented_imports) + "\n)"
        
        # 检查是否是函数调用或参数列表
        if "(" in line and ")" in line:
            # 尝试在括号处换行
            open_idx = line.find("(")
            if open_idx > 0 and open_idx < max_length:
                prefix = line[:open_idx+1]
                suffix = line[open_idx+1:]
                
                # 计算缩进
                indent = len(prefix) - len(prefix.lstrip())
                indent_str = " " * (indent + 4)  # 额外缩进4个空格
                
                # 处理参数
                if "," in suffix:
                    params = suffix.split(",")
                    formatted_params = []
                    for param in params:
                        param = param.strip()
                        if param:
                            formatted_params.append(param)
                    
                    if formatted_params:
                        return prefix + "\n" + indent_str + (",\n" + indent_str).join(formatted_params)
        
        # 检查是否是字符串连接
        if "+" in line and ('"' in line or "'" in line):
            parts = re.split(r'(\s*\+\s*)', line)
            if len(parts) > 1:
                result = parts[0]
                current_length = len(result)
                
                for i in range(1, len(parts), 2):
                    if i+1 < len(parts):
                        operator = parts[i]
                        next_part = parts[i+1]
                        
                        if current_length + len(operator) + len(next_part) > max_length:
                            # 需要换行
                            result += operator.rstrip() + "\n    " + next_part.lstrip()
                            current_length = 4 + len(next_part.lstrip())  # 4是缩进
                        else:
                            result += operator + next_part
                            current_length += len(operator) + len(next_part)
                
                return result
        
        # 如果无法优化，返回原始行
        return line
    
    def _optimize_long_line_js(self, line: str) -> str:
        """
        优化过长的JavaScript行
        
        Args:
            line: 原始行
            
        Returns:
            优化后的行
        """
        max_length = self.config.get("max_line_length", 100)
        if len(line) <= max_length:
            return line
        
        # 检查是否是导入语句
        if re.match(r'^\s*import\s+', line):
            # 处理导入语句
            if "{" in line and "}" in line:
                # 将大括号内的导入拆分为多行
                before_brace = line.split("{")[0]
                after_brace = line.split("}", 1)[1]
                imports = line.split("{")[1].split("}")[0].split(",")
                indented_imports = [imp.strip() for imp in imports if imp.strip()]
                return before_brace + "{\n  " + ",\n  ".join(indented_imports) + "\n}" + after_brace
        
        # 检查是否是函数调用或参数列表
        if "(" in line and ")" in line:
            # 尝试在括号处换行
            open_idx = line.find("(")
            if open_idx > 0 and open_idx < max_length:
                prefix = line[:open_idx+1]
                suffix = line[open_idx+1:]
                
                # 计算缩进
                indent = len(prefix) - len(prefix.lstrip())
                indent_str = " " * (indent + 2)  # 额外缩进2个空格
                
                # 处理参数
                if "," in suffix:
                    params = suffix.split(",")
                    formatted_params = []
                    for param in params:
                        param = param.strip()
                        if param:
                            formatted_params.append(param)
                    
                    if formatted_params:
                        return prefix + "\n" + indent_str + (",\n" + indent_str).join(formatted_params)
        
        # 检查是否是对象字面量
        if "{" in line and "}" in line and ":" in line:
            # 尝试在大括号处换行
            open_idx = line.find("{")
            close_idx = line.rfind("}")
            
            if open_idx >= 0 and close_idx > open_idx:
                prefix = line[:open_idx+1]
                content = line[open_idx+1:close_idx]
                suffix = line[close_idx:]
                
                # 计算缩进
                indent = len(prefix) - len(prefix.lstrip())
                indent_str = " " * (indent + 2)  # 额外缩进2个空格
                
                # 处理属性
                if "," in content:
                    props = content.split(",")
                    formatted_props = []
                    for prop in props:
                        prop = prop.strip()
                        if prop:
                            formatted_props.append(prop)
                    
                    if formatted_props:
                        return prefix + "\n" + indent_str + (",\n" + indent_str).join(formatted_props) + "\n" + " " * indent + suffix
        
        # 如果无法优化，返回原始行
        return line